////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file camxmemspy.h
///
/// @brief CamX MemSpy declarations. MemSpy is used to track memory allocations.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef CAMXMEMSPY_H
#define CAMXMEMSPY_H

#include "camxincs.h"
#include "camxlist.h"

// Only compile in if enabled
#if CAMX_USE_MEMSPY

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Forward declarations
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct MemSpyAllocDesc;
struct MemSpyTrackingInfo;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief The MemSpy class is used to track memory allocations.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class MemSpy
{
public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Public Methods
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// AdjustAllocSize
    ///
    /// @brief  This function adjusts the allocation size to accommodate the MemSpy tracking information.
    ///
    /// @param  size        The size to be adjusted.
    /// @param  alignment   The byte alignment required. This must be a power of 2. A value of 0 indicates no alignment is
    ///                     required.
    /// @param  pageSize    The size of a virtual memory page used to pad the input size to a multiple of a page size (for
    ///                     freed-write detection). A size of 0 indicates that no page-size padding is required.
    ///
    /// @return The adjusted size.
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static SIZE_T AdjustAllocSize(
        SIZE_T  size,
        UINT32  alignment,
        UINT32  pageSize);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// TrackAlloc
    ///
    /// @brief  This function adds a memory allocation to the list to be tracked.
    ///
    /// @param  pMem        Pointer to the allocated memory to track
    /// @param  allocSize   Actual size of the allocation after adding padding
    /// @param  clientSize  Size of the allocation requested by client
    /// @param  alignment   The byte alignment required. This must be a power of 2. A value of 0 indicates no alignment is
    ///                     required.
    /// @param  flags       Flags indicating the type of the allocation
    /// @param  type        Type of the allocated memory
    /// @param  pFileName   Name of the file in which the allocation was called
    /// @param  lineNum     Line number at which the allocation was called
    ///
    /// @return New allocation pointer which hides the MemSpy info.
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static VOID* TrackAlloc(
        VOID*           pMem,
        SIZE_T          allocSize,
        SIZE_T          clientSize,
        UINT32          alignment,
        CamxMemFlags    flags,
        CamxMemType     type,
        const CHAR*     pFileName,
        UINT            lineNum);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// TrackFree
    ///
    /// @brief  This function removes a memory allocation from the list of tracked memory allocations.
    ///
    /// @param  pMem        Pointer to the allocated buffer to no longer track
    /// @param  flags       Flags indicating the type of the allocation
    /// @param  pFileName   Name of the file in which the allocation was called
    /// @param  lineNum     Line number at which the allocation was called
    ///
    /// @return Pointer that must be passed into the CAMX_FREE* function.
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static VOID* TrackFree(
        VOID*           pMem,
        CamxMemFlags    flags,
        const CHAR*     pFileName,
        UINT            lineNum);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PrintReport
    ///
    /// @brief  This function is called to print a report of the current memory usage status.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static VOID PrintReport();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PrintRuntimeReport
    ///
    /// @brief  Prints memory usage statistics for runtime footprint tracking
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static VOID PrintRuntimeReport();

private:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Private Methods
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetInstance
    ///
    /// @brief  This function returns the singleton instance of MemSpy.
    ///
    /// @return A pointer to the singleton instance of MemSpy
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static MemSpy* GetInstance();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// MemSpy
    ///
    /// @brief  Private constructor for a MemSpy instance
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    MemSpy();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ~MemSpy
    ///
    /// @brief  Private destructor for a MemSpy instance
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ~MemSpy();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetTrackingInfo
    ///
    /// @brief  Common function to extract tracking info to be used in other functions
    ///
    /// @param  pClientPtr  The pointer returned to the client from TrackAlloc()
    ///
    /// @return A pointer to the tracking info associated with the memory pointer, if valid.
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    MemSpyTrackingInfo* GetTrackingInfo(
        VOID* pClientPtr);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PrintMemStats
    ///
    /// @brief  Prints memory usage statistics
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID PrintMemStats();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CheckMemLeaks
    ///
    /// @brief  Checks for memory leaks and prints report if leak detected.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID CheckMemLeaks();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CheckMemAccumulation
    ///
    /// @brief  Checks for memory accumulation that could result in out-of-memory errors.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID CheckMemAccumulation();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GenerateReport
    ///
    /// @brief  Generates and prints the final report
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID GenerateReport();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GenerateRuntimeReport
    ///
    /// @brief  Generates and prints the runtime report
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID GenerateRuntimeReport();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetFreeNode
    ///
    /// @brief  Gets an LDLLNode
    ///
    /// @return LDLLNode available to insert in another list
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    LDLLNode* GetFreeNode()
    {
        LDLLNode* pNode = m_freedAllocList.RemoveFromHead();
        if (NULL == pNode)
        {
            pNode = static_cast<LDLLNode*>(CAMX_CALLOC_NO_SPY(sizeof(LDLLNode)));
        }
        return pNode;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PutFreeNode
    ///
    /// @brief  Returns an LDLLNode
    ///
    /// @param  pNode Node to free
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID PutFreeNode(
        LDLLNode* pNode)
    {
        CAMX_ASSERT(pNode != NULL);

        pNode->pData = NULL;
        m_freedAllocList.InsertToTail(pNode);
    }

    // Do not implement the copy constructor or assignment operator
    MemSpy(const MemSpy& rMemSpy) = delete;
    MemSpy& operator= (const MemSpy& rMemSpy) = delete;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Private Data
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    LightweightDoublyLinkedList m_availableNodeList;       ///< List of available LDLLNodes
    LightweightDoublyLinkedList m_activeAllocList;         ///< List of active allocations
    LightweightDoublyLinkedList m_allocCallsiteList;       ///< List of call sites from which allocations occur

#if CAMX_DETECT_WRITE_TO_FREED_MEM
    LightweightDoublyLinkedList m_freedAllocList;          ///< List of freed allocations
#endif // CAMX_DETECT_WRITE_TO_FREED_MEM

    Mutex*                      m_pMemSpyLock;             ///< Mutex to protect access from multiple threads

    SIZE_T                      m_totalNumBytesInUse;      ///< Total number of bytes in use
    UINT                        m_totalNumAllocsInUse;     ///< Total number of allocations currently active
    SIZE_T                      m_totalNumBytesWatermark;  ///< Total number of bytes in use high watermark
    UINT                        m_totalNumAllocsWatermark; ///< Total number of allocs that make up m_totalNumBytesWatermark

    UINT64                      m_currentTime;             ///< Current time

    static BOOL                 s_isValid;                 ///< whether the MemSpy singleton is in a valid state
};

CAMX_NAMESPACE_END

#endif // CAMX_USE_MEMSPY

#endif // CAMXMEMSPY_H
